home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 201-225 / 216 / cloud / cloud.doc < prev    next >
Text File  |  1995-03-13  |  16KB  |  419 lines

  1.  
  2.  
  3.                 Cloud
  4.  
  5.     "Cloud" is a program that generates and displays fractal surfaces.
  6.     I had briefly looked at 'sc', but found that it was too slow to
  7.     be interesting.  I read about generating fractal surfaces and 
  8.     displaying them as clouds in the (technical) book "Fractals", by
  9.     Jens Feder (ISBN 0-306-42851-2), section 13.4 "Random Addition
  10.     Surfaces".  So I thought I'd try it myself.  What follows are
  11.     some explanations of the program's user interface, and some of
  12.     its workings.      The clouds that look like these images are called
  13.     "fracto-cumulus" by the way.
  14.  
  15.     Requirements:  It requires about 200K to run - I've tried it on an 
  16.     A1000 with expansion memory turned off, and it works OK.
  17.  
  18. Cloud brings up a custom lo-res screen (320x200).  The screen is in two
  19. sections - the right 3/4 is where the image is displayed.  On the left of 
  20. the screen is a control panel with a bunch of gadgets. Most of these gadgets
  21. are grouped into three little panels.  From the top of the window downwards,
  22. these buttons are:
  23.  
  24.     Standard Window Close Gadget:
  25.         Exit Cloud by clicking on the window's close gadget.
  26.  
  27.     Auto - toggle to select the automatic generate and display loop.
  28.         When "auto" is set, the program will loop between generating
  29.         new images and displaying them.  To stop, toggle "auto" back
  30.         off.  To pause, toggle "pause".
  31.  
  32.     Pause - toggle to pause while in the automatic loop.
  33.         This pauses the program while in the automatic loop.
  34.         You can then select other palettes.
  35.  
  36.     Save - this will let you save the current image as an IFF file,
  37.         when it is implemented.
  38.  
  39.     '  ' - (there is an empty, unused gadget beneath Save)
  40.  
  41.     Rand - toggle to turn on the randomizer.
  42.         This enables a section of code that adjusts the random
  43.         number generator.  If you don't turn this on, you will
  44.         get the same sequence of images each time you run Cloud.
  45.  
  46.     Scroll - toggle to have the display scroll up from the bottom.
  47.         When this is off, the new image fills in from the top
  48.         of the screen downwards.  When it is on, the screen is
  49.         scrolled upwards, with the old image disappearing off the
  50.         top, and the new image appearing on the bottom.  It adds
  51.         "lightning" to the clouds.
  52.  
  53.     Gen - to generate and display a new image.
  54.         This generates a new image, and displays it.
  55.  
  56.     Re-Do - to re-display the present image.
  57.         This displays the current image again, which is useful to
  58.         see the effects the different scalings can have on the
  59.         same image.
  60.  
  61.     "d = XX %" - place to enter the delta percent value.
  62.         This string gadget accepts a two digit number whose value
  63.         is used in generating new images.  The gadget hit box
  64.         surrounds the digits.  The value determines the smoothness
  65.         of the image.  For clouds, a good value is 80%, for terrain
  66.         and water, 70% is better.  Lower and higher values are also
  67.         interesting, if less natural.
  68.  
  69.     Log - selects a logarithmic scaling,
  70.     Lin - selects a linear scaling,
  71.     Exp - selects an exponential scaling,
  72.     Histo - selects an equal area scaling between data and colors.
  73.         You choose one of these four buttons to select the type
  74.         of scaling performed to map the image data to the screen.
  75.         There's more on this later.
  76.  
  77.     Color Bar - shows the current image palette.
  78.         This isn't a gadget, it just shows you what the palette is.
  79.  
  80.     Reverse    - flips the color palette end for end.
  81.         By flipping the palette over, the image seems to reverse;
  82.         dark things become light, and light sections become dark.
  83.  
  84.     Atmos - selects the "cloud in the sky" color palette,
  85.     Earth - selects the Rand McNally map palette,
  86.     Water - selects an "islands in oceans" palette,
  87.     Therm - selects a palette that is reminiscient of thermal images.
  88.         Choose one of these four buttons to select the color palette
  89.         for displaying the image.  These are active at all times,
  90.         so that while generating the next image, you can fiddle with
  91.         the one currently on the screen.
  92.  
  93. -------------------------------------------------
  94. About the color palettes (or do you say palette?):
  95.  
  96. The following table lists the color trends in each of the palettes.
  97.  
  98. Color range    Atmos        Earth        Water        Therm
  99.         -----        -----        -----        -----
  100.   HI        white        near white    light green    red
  101.   ^         ...        dark brown    sand        orange
  102.   |        lighter blue    light brown    blue-green    yellow
  103.   |         ...        green        light blue    green
  104.   v        lighter blue    sand        blue        blue
  105.   LO        blue        lake blue    deep blue    dark blue
  106.  
  107. Data means:    density        height        depth        temperature
  108.  
  109. If you're running Workbench 1.3, you can use the Palette command to change
  110. Cloud's colors.  Put the Cloud screen in the front, pull it down to expose
  111. a CLI window or the drawer containing the "Palette" command, and invoke
  112. "Palette".  You can then change Cloud's colors temporarily - the color changes
  113. are not remembered by Cloud.  Also, there will be a white rectangle left over
  114. from where the Palette window was.  Just click on the Re-Do gadget to redraw
  115. the image.
  116.  
  117. -----------------------------
  118. About the scaling selections:
  119.  
  120. The data can be mapped to the colors in    many ways - I've chosen 4: linear,
  121. log, exponential, and equal area.   Each has its own good and bad points.
  122.  
  123. The linear scaling makes a hazy cloud, but it leaves the Earth landscape
  124. flat and boring.  This is because the majority of the data values get
  125. mapped to the middle range of the palette - so we don't see much of the
  126. peaks and pits.
  127.  
  128. The log scaling tends to expand the lower data values over a larger color
  129. range and compress the higher values into the higher colors.  It emphasizes
  130. the upper data values at the expense of the lower ones.  It makes the sky
  131. seem even more hazy.
  132.  
  133. Exponential scaling is the reverse of log scaling.  The higher data values
  134. will be spread across a greater color range, as compared with linear scaling,
  135. and the lower data values will be compressed into the lowest color(s).  It
  136. emphasizes the lower data values at the expense of the upper ones.  The sky
  137. becomes more blue with this setting.
  138.  
  139. The equal-area scaling uses a histogram to find out the distribution of the
  140. data values.  It then maps the data values to colors such that each color
  141. occurs as often as every other color.  It seems to reveal lots of detail,
  142. and so it makes Therm and Water busy looking, but it makes Atmos look better.
  143. It emphasizes the extremes of the data, both upper and lower, at the expense
  144. of the mid-range values.  The sky looks most natural with this setting - 
  145. patches of blue sky and patches of white clouds with quick transitions between.
  146.  
  147. -----------------------------
  148. About the scaling algorithms:
  149.  
  150. LINEAR scaling maps the data values to palette color positions (pen numbers)
  151. with the equation:
  152.                    Delta-C
  153.         C =  (V - VMin) *  -------  +  LoColor
  154.                        Delta-V
  155.  
  156. This divides the range VMin to VMax into NColor sections (where NColor = 
  157. HiColor - LoColor + 1).  Actually, instead of doing that equation over 
  158. 66000 times, I build a color lookup table, from the minimum data value to 
  159. the maximum data value, and use that equation NColor (13) times to fill the
  160. table.
  161.  
  162. LOGARITHMIC scaling finds the multiplicative factor such that:
  163.  
  164.                 NColor
  165.         VMax = VMin * X
  166.  
  167. This will divide the range VMin to VMax into NColor sections, each of which
  168. is X times larger than the previous.  X can be found by:
  169.  
  170.                    (1 / NColor)
  171.         X = (VMax/VMin)    
  172.  
  173. The EXPONENTIAL scaling uses the logarithmic routine to first build the
  174. color table for the logarithmic scaling.  Then it flips the table around
  175. end-for-end, and top-to-bottom.  This changes the up-and-over logarithmic
  176. curve to the over-and-up exponential curve.
  177.  
  178. HISTOGRAM scaling produces an equal-area scaling, where the range VMin to
  179. VMax is divided into NColor sections, each of which has the same number
  180. of image points within it.  To do this, a histogram is made from the image
  181. data.  The histogram is a table that  represents how many data points occur 
  182. for each data value, over all the data values (from VMin to VMax), i.e. how
  183. many data elements are at a certain height for all heights.  (Typically, the
  184. histogram looks like the infamous "bell-shaped" curve used by school teachers
  185. to place exam grades "on the curve".  This is that curve.)  Next, the histogram
  186. is summed along its values - changing the bell-curve into an stretched out
  187. S-curve:
  188.              -                      ----        -
  189.           -     -                          --        -
  190.         -         -        -->             --            -
  191.     ---             ---            ----            -
  192.  
  193. Given this final table, we can divide the vertical span of the table into
  194. NColor sections.  Working towards the graph line and then down to the baseline,
  195. we find the NColor data values that divide the original image into NColor
  196. equal-area pieces.  From this information, we build the color lookup table.
  197.  
  198. Note that in all of these scaling algorithms, it is much quicker at run time
  199. to build a 1024 element table once, than it is to repeatedly perform the
  200. same calculation over 66000 times.  The cost is in memory use: an extra
  201. 1KB for the lookup table and 4KB for the histogram seem worth it.
  202.  
  203. The only routine that uses floating point arithmetic is the Logarithmic 
  204. scaling routine.  It uses the "pow()" function and floating point 
  205. multiplication and floating-point to integer conversions.  Roughly two dozen 
  206. explicit floating-point operations are needed.  All other routines throughout
  207. the program use integer arithmetic.
  208.  
  209. --------------------------------
  210. The central algorithm for Cloud:
  211.  
  212. (This is mostly a historical account of how the generator evolved, as well
  213. as a summary for myself...:-)
  214.  
  215. Assumptions:
  216.     - the generated image will be square, size of 2^N + 1 (257 x 257)
  217.     - x axis runs left-right, y axis goes up-down
  218.  
  219. Data generation:
  220.     - there are N iterations
  221.     - each iteration has two parts
  222.         - adding random values to elements of the array,
  223.         - generating new elements by averaging between the old ones
  224.     - during each iteration the "step" between elements is halved
  225.     - between iterations the range of the random value is decreased
  226.     - we're finished when the step size becomes 1
  227.     - the initial step size is 2^N (first iteration does the 4 corners)
  228.  
  229. Data display:
  230.     - the max and min are found
  231.     - one of four routines are used to fill a Color LookUp table
  232.     - each point in the array is translated by the color lookup table
  233.       into a pen color which is written to the respective screen pixel
  234.  
  235. -------------------
  236. Generation example:
  237.                 ------------- NUMBER OF ELEMENTS -----------
  238. Iteration    Step        Add random    Average new    Total points
  239. 1        N    (256)    4        5        9    (3x3)
  240. 2        N/2    (128)    9        16        25    (5x5)
  241. 3        N/4    ( 64)                        (9x9)
  242. 4        N/8    ( 32)                    (17x17)
  243. 5        N/16    ( 16)                    (33x33)
  244. 6        N/32    (  8)                    (65x65)
  245. 7        N/64    (  4)                    (129x129)
  246. 8        N/128    (  2)                    (257x257)
  247.  
  248. ---------------------
  249. Generation algorithm:
  250.  
  251. The first 4 elements:        Generate 5 more, to make 9:
  252. (256 pts apart)            (now 128 pts apart)
  253.     o           o        o     -     o    
  254.  
  255.                 |     X     |
  256.  
  257.     o           o        o     -     o    
  258.  
  259. Next iteration:
  260.  
  261. Take 9 elements:        Generate 16 more, to make 25:
  262. (128 pts apart)            (now 64 pts apart)
  263.     o     o     o        o  -  o  -  o
  264.                 |  X  |  X  |
  265.     o     o     o        o  -  o  -  o
  266.                 |  X  |  X  |
  267.     o     o     o        o  -  o  -  o
  268.  
  269. At each step, the character shows what to do at that element:
  270.     "o" is an old element, 
  271.         on the first pass we add a random value to it, 
  272.         then on the second pass we use it to generate the new elements
  273.     "-" is a new element, 
  274.         it is generated by averaging the left and right neighbors
  275.     "|" is a new element,
  276.         generated by averaging the top and bottom neighbors
  277.     "X" is a new element,
  278.         generated by averaging the neighbors in the surrounding corners
  279.  
  280. The initial algorithm went like this:
  281.  
  282.     randrange = rrmax
  283.     stepsize = MAX
  284.     while ( stepsize > 1 )
  285.     do
  286.     /* first elevate the random points */
  287.  
  288.     for y in the set {0, stepsize, 2*stepsize,... MAX}
  289.     for x in the set {0, stepsize, 2*stepsize,... MAX}
  290.         data[x,y] += random_value
  291.     end x
  292.     end y
  293.  
  294.     /* now, adjust the stepsize, and generate the new points */
  295.  
  296.     stepsize /= 2
  297.  
  298.     for y in the set {0, stepsize, 2*stepsize,... MAX}
  299.         if ( y is "odd" )    /* generate the "| X | X |..." averages */
  300.         for x in the set {0, 2*stepsize, 4*stepsize,... MAX"}
  301.         data[x,y] = ( data[x,y-1 ] + data[x, y+1] ) / 2  /* do |'s */
  302.         for x in the set {stepsize, 3*stepsize,... (MAX)}
  303.         data[x,y] = ( data[x-1,y-1] + data[x-1,y+1] +    /* do X's */
  304.               data[x+1,y-1] + data[x+1,y+1] ) / 4
  305.  
  306.         else        /* y is "even", generate "o - o - o ..."  */
  307.         for x in the set {stepsize, 3*stepsize,... (MAX)}
  308.             data[x,y] = ( data[x-1,y] + data[x+1,y] ) / 2
  309.     end y
  310.  
  311.     randrange = randrange * fraction
  312.     done        
  313.  
  314. In the present program, all array indexing is avoided, and the core algorithm 
  315. is implemented differently.  One problem with the above algorithm is that
  316. each old element is accessed 8 times when generating new elements. 
  317. You can see this by looking at the general pattern around an old element:
  318.  
  319.     X | X
  320.     - o -        <-- this `o' gets bothered 8 times!
  321.     X | X
  322.         
  323. We can take another look and use a different pattern for our core routine.
  324. Instead of having different actions for each row, we can treat the array
  325. as a set of "cells":
  326.  
  327.     ... o - o ...        Or, giving them names:    NW  N  NE
  328.     ... | X |                     W  C  E
  329.     ... o - o ...                    SW  S  SE
  330.  
  331. where C is the center of the cell, and the other corners and sides are
  332. named after the compass points.
  333.  
  334. We can save a lot of unnecessary fetches by realizing that the East side
  335. of the current cell is the West side of the next cell.  In this way, the
  336. old elements are fetched only twice apiece - once for the cells below them,
  337. and once for the cells above them.
  338.  
  339. Our computational cell is:
  340.                     Generate these:        From these:
  341.     (NW)    NE  =>    (NW)    NE                (NW)    NE
  342.       W  C    E          W  C    E        C  E            
  343.     (SW) S    SE  =>     (SW) S    SE        S        (SW)    SE
  344.       current ---->    next   
  345.  
  346. From the last cell (to the left), we have NW and SW.  For this current cell 
  347. we fetch NE and SE.  We compute S, C, and E, and store them.  To prepare for 
  348. the next cell (to the right), we update NW from NE, and SW from SE.  We then 
  349. move our pointers to go to the next cell.
  350.  
  351. Now the core algorithm becomes:
  352.  
  353.     Generate the first row of "o - o - ... o" separately.
  354.     Get pointers to W and C.
  355.     Pick up NW, SW; average them and store at W.
  356.     Shift the W pointer to now point to E.
  357.     For all the rows:
  358.         For the cells in this row:
  359.             Pick up NE, SE.
  360.             Generate the various averages, and store at S, C, E.
  361.                 S = (SE + SW)/2
  362.                 E = (NE + SE)/2
  363.                 C = (NE + SE + SW + NW)/4
  364.             Shift SE to SW, shift NE to NW.
  365.             Shift C pointer to next C (to the right).
  366.             Shift E pointer to next E (to the right).
  367.  
  368. There is one final trick -  we can see that C can be generated in many
  369. ways, and one way is:  C = (E + W)/2.  We can adjust the algorithm to
  370. remember W and SW, instead of NW and SW.  The advantage is there's slightly
  371. less bother when indexing off the pointers.  Also, I use two times W, i.e.
  372. before it is halved.  This way there is less problem of truncation in 
  373. computing element C.  The inner core equations finally become:
  374.  
  375.             Pick up NE, SE
  376.             Form E2 = NE + SE
  377.             Store E = E2/2
  378.             Store C = (W2 + E2)/4
  379.             Store S = (SE + SW)/2
  380.             Update SW from SE
  381.             Update W2 from E2
  382.             Update C and E pointers
  383.  
  384. And that's why you don't have to wait very long for a new image.
  385.  
  386. ---------------------------
  387. About that "delta percent":
  388.  
  389. The integer gadget lets you change the amount that the random numbers
  390. influence the evolving surface.  In the initial algorithm given above, 
  391. the range of the random numbers was changed at each iteration by the 
  392. statement:
  393.         randrange = randrange * fraction
  394.  
  395. The value of "delta percent" gets turned into `fraction'.  What it means
  396. is that at each iteration, the range of random numbers is decreased by
  397. "delta percent".  If it is 80% (default), then the range of random numbers
  398. decreases like this: 100, 80, 64, 51, 41, 33, 26, 21 (for 8 iterations).
  399. With higher values, the resulting image is "bumpier"; with lower values
  400. it is smoother.  Lower values are useful to help you get a feel for how
  401. the scaling types affect the image.  I find that the images are too noisy
  402. with values above 80, and have a nice smoothness with values around 50.
  403. Below 40 the images become too simple to be interesting in themselves.
  404.  
  405. ----------------
  406. Some last words:
  407.  
  408. I hope you like 'Cloud' as much as I do.  It was fun to watch it grow
  409. and evolve.  Bugs, problem reports, and suggestions for improvements
  410. can be sent to:
  411.  
  412. Mail:    Mike Hall    
  413.     2 S 461 Cherice Dr.
  414.     Warrenville, IL  60555
  415. UUCP: att!cuuxb!migh
  416.  
  417. Thanks!
  418.  
  419.